package com.serviceprovider.business.module.report.controller;

//~--- non-JDK imports --------------------------------------------------------

import com.serviceprovider.business.module.admin.user.services.dao.interfaces.IUserService;
import com.serviceprovider.business.module.order.model.OrderStatus;
import com.serviceprovider.business.module.order.services.interfaces.IOrderService;
import com.serviceprovider.business.module.order.services.interfaces.IOrderStatusService;
import com.serviceprovider.business.module.report.model.CustomReport;
import com.serviceprovider.business.module.report.services.dao.interfaces.ICustomReportService;
import com.serviceprovider.business.module.report.services.dao.interfaces.IReportService;
import com.serviceprovider.business.module.report.valueobjects.AllFeeReportData;
import com.serviceprovider.business.module.report.valueobjects.ReportGenerationCriteria;
import com.serviceprovider.business.module.report.valueobjects.SelfFeeReportData;
import com.serviceprovider.business.module.sp.transaction.model.TransactionDetail;
import com.serviceprovider.business.module.sp.transaction.services.interfaces.ITransactionUserAmountService;
import com.serviceprovider.business.module.user.model.impl.User;
import com.serviceprovider.business.utility.ApplicationConfigProperties;
import com.serviceprovider.business.utility.RequestConstants;
import com.serviceprovider.business.utility.SecurityContextUser;
import com.serviceprovider.business.utility.maintenance.settings.AppSettings;
import com.serviceprovider.business.utility.maintenance.settings.RoleUtils;

import org.apache.log4j.Logger;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.propertyeditors.CustomDateEditor;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;

//~--- JDK imports ------------------------------------------------------------

import java.text.SimpleDateFormat;

import java.util.*;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

/**
 * Used to process view report's request
 * @author mrduongnv@gmail.com
 *
 */
@Controller()
public class ReportsController {
    private static Logger       logger      = Logger.getLogger(ReportsController.class);
    private static List<String> ReportTypes = new ArrayList<String>();

    static {
        ReportTypes.add(AppSettings.ALL_FEES_REPORT);
        ReportTypes.add(AppSettings.SELF_FEE_REPORT);
        ReportTypes.add(AppSettings.CUSTOM_FEE_REPORT);
    }

    @Autowired
    private ICustomReportService          customReportService;
    @Autowired
    private IOrderService                 orderServiceImpl;
    @Autowired
    private IOrderStatusService           orderStatusService;
    private List<User>                    payees;
    @Autowired
    ApplicationConfigProperties           properties;
    private List<User>                    providers;
    @Autowired
    IReportService                        reportService;
    private List<OrderStatus>             reportStatus;
    @Autowired
    private ITransactionUserAmountService transactionUserAmountService;
    @Autowired
    private IUserService                  userService;

    @InitBinder
    public void initBinder(WebDataBinder binder) {
        SimpleDateFormat dateFormat = new SimpleDateFormat(properties.getDateFormat());

        dateFormat.setLenient(false);
        binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, false));
    }

    @RequestMapping(
        value  = RequestConstants.ReportsRequests.REPORTS,
        method = RequestMethod.GET
    )
    public ModelAndView reportsView() {
        ModelAndView             mav            = new ModelAndView(RequestConstants.ReportsRequests.VIEW_REPORTS);
        ReportGenerationCriteria criteria       = new ReportGenerationCriteria();
        Calendar                 firstDayOfYear = Calendar.getInstance();

        firstDayOfYear.set(firstDayOfYear.get(Calendar.YEAR), Calendar.JANUARY, 1);    // Make it 1.01 of the year
        criteria.setDateFrom(firstDayOfYear.getTime());
        criteria.setDateTo(new Date());

        String  userEmail          = SecurityContextUser.getUserName();
        User    user               = userService.getByEmail(userEmail);
        Integer selectedProviderId = null;

        if (SecurityContextUser.isAdmin()) {
            providers = orderServiceImpl.findAllProviders();
            mav.addObject("providers", providers);
        } else {
            selectedProviderId = user.getId();
        }

        payees = transactionUserAmountService.findPayeesOfProvider(selectedProviderId);
        mav.addObject("payees", payees);
        mav.addObject("criteria", criteria);
        mav.addObject("statuses", getStatuses());
        mav.addObject("reportTypes", ReportTypes);
        mav.addObject("selectedFee", TransactionDetail.FEE_COLUMN_NAMES);

        return mav;
    }

    @RequestMapping(
        value  = RequestConstants.ReportsRequests.CUSTOM_REPORT,
        method = RequestMethod.GET
    )
    public ModelAndView customReport(HttpSession sess) {
        ModelAndView mav      = new ModelAndView(RequestConstants.ReportsRequests.VIEW_CUSTOM_FEES_REPORT);
        CustomReport criteria = new CustomReport();

        criteria.setDateFrom(new Date());
        criteria.setDateTo(new Date());

        List<OrderStatus> statuses   = getStatuses();
        String            userEmail  = SecurityContextUser.getUserName();
        User              user       = userService.getByEmail(userEmail);
        Integer           providerId = null;

        if ((user.getRole() != null) && RoleUtils.ID_ADMIN_ROLE.equals(user.getRole().getId())) {
            providers = orderServiceImpl.findAllProviders();
            mav.addObject("providers", providers);
        } else {
            providerId = user.getId();
        }

        payees = transactionUserAmountService.findPayeesOfProvider(providerId);

        List<CustomReport> listAvailableReports = customReportService.listByUser(SecurityContextUser.isAdmin()
                ? null
                : userEmail);

        mav.addObject("payees", payees);
        mav.addObject("criteria", criteria);
        mav.addObject("statuses", statuses);
        mav.addObject("reportTypes", ReportTypes);
        mav.addObject("selectedFee", TransactionDetail.FEE_COLUMN_NAMES);
        mav.addObject("listReports", listAvailableReports);

        return mav;
    }

    @RequestMapping(
        value  = RequestConstants.ReportsRequests.CUSTOM_REPORT,
        method = RequestMethod.POST
    )
    public ModelAndView customReport(HttpSession sess, @ModelAttribute CustomReport criteria) {
        ModelAndView      mav        = new ModelAndView(RequestConstants.ReportsRequests.VIEW_CUSTOM_FEES_REPORT);
        List<OrderStatus> statuses   = getStatuses();
        String            userEmail  = String.valueOf(SecurityContextUser.getUserName());
        User              user       = userService.getByEmail(userEmail);
        Integer           providerId = null;

        if ((user.getRole() != null) && RoleUtils.ID_ADMIN_ROLE.equals(user.getRole().getId())) {
            providers = orderServiceImpl.findAllProviders();
            mav.addObject("providers", providers);
        } else {
            providerId = user.getId();
        }

        payees = transactionUserAmountService.findPayeesOfProvider(providerId);
        customReportService.save(criteria);

        List<CustomReport> listAvailableReports = customReportService.listByUser(SecurityContextUser.isAdmin()
                ? null
                : userEmail);

        mav.addObject("payees", payees);
        mav.addObject("criteria", criteria);
        mav.addObject("statuses", statuses);
        mav.addObject("reportTypes", ReportTypes);
        mav.addObject("selectedFee", TransactionDetail.FEE_COLUMN_NAMES);
        mav.addObject("listReports", listAvailableReports);

        return mav;
    }

    @RequestMapping(
        value  = RequestConstants.ReportsRequests.REPORTS,
        method = RequestMethod.POST
    )
    public ModelAndView reportSelection(HttpSession sess, HttpServletRequest req,
            @ModelAttribute ReportGenerationCriteria criteria) {
        String userEmail = String.valueOf(SecurityContextUser.getUserName());

        if (criteria.getProviderId() == null) {
            criteria.setProviderId(getCurrentUserId(userEmail));
        }

        ModelAndView      mav      = generateReport(criteria.getReportType(), criteria);
        List<OrderStatus> statuses = getStatuses();

        // TODO:
        User provider   = userService.getUserConnected();
        User level1user = provider.getLevel1User();

        mav.addObject("level1userid", level1user.getId());
        mav.addObject("level1username", level1user.getEmail());
        mav.addObject("criteria", criteria);
        mav.addObject("statuses", statuses);
        mav.addObject("reportTypes", ReportTypes);

        return mav;
    }

    @RequestMapping(
        value  = RequestConstants.ReportsRequests.RUN_CUSTOM_REPORT,
        method = RequestMethod.POST
    )
    public ModelAndView customReport(HttpSession sess, HttpServletRequest req,
                                     @ModelAttribute ReportGenerationCriteria criteria) {
        return reportSelection(sess, req, criteria);
    }

    @RequestMapping(
        value  = RequestConstants.ReportsRequests.RUN_CUSTOM_REPORT,
        method = RequestMethod.GET
    )
    public ModelAndView customReport(HttpSession sess, HttpServletRequest req) {
        ReportGenerationCriteria criteria = new ReportGenerationCriteria();
        int                      reportId = 0;

        try {
            reportId = Integer.parseInt(req.getParameter("id"));
        } catch (Exception ex) {}

        CustomReport customReport = customReportService.get(reportId);

        if (customReport != null) {
            if (logger.isDebugEnabled()) {
                logger.debug("Found custom report id = " + reportId);
            }

            criteria.setReportType(customReport.getReportType());
            criteria.setProviderId(customReport.getUserId());

            User user = userService.getById(customReport.getUserId());

            criteria.setLoginUserEmail(user.getEmail());
            criteria.setDateFrom(customReport.getDateFrom());
            criteria.setDateTo(customReport.getDateTo());
            criteria.setPayees(customReport.getPayeesId());
            criteria.setSelectedFee(customReport.getSelectedFees().toArray(new String[] {}));

            if (customReport.getTransactionStatus().equals("")) {
                OrderStatus orderStatus = orderStatusService.get(AppSettings.ORDER_STATUS_NEW_ID);

                criteria.setStatus(orderStatus.getId());
            } else {
                criteria.setStatus(Integer.parseInt(customReport.getTransactionStatus()));
            }
        }

        return reportSelection(sess, req, criteria);
    }

    @RequestMapping(
        value  = RequestConstants.ReportsRequests.DELETE_CUSTOM_REPORT,
        method = RequestMethod.GET
    )
    public ModelAndView deleteCustomReport(HttpSession sess, HttpServletRequest req) {
        int reportId = 0;

        try {
            reportId = Integer.parseInt(req.getParameter("id"));
        } catch (Exception ex) {}

        customReportService.delete(reportId);

        ModelAndView mav = customReport(sess);

        mav.setViewName("redirect:" + RequestConstants.ReportsRequests.CUSTOM_REPORT);

        return mav;
    }

    private List<OrderStatus> getStatuses() {
        if (reportStatus == null) {
            reportStatus = orderStatusService.findAll();
        }

        return reportStatus;
    }

    /**
     * Return the value which is used to display transaction report
     */
    private ModelAndView generateReport(String reportType, ReportGenerationCriteria criteria) {
        if (logger.isDebugEnabled()) {
            logger.debug("Service Provider email: " + criteria.getLoginUserEmail() + ", Service Provider ID: "
                         + criteria.getProviderId() + ", Payee IDs:" + String.valueOf(criteria.getPayees()));
        }

        ModelAndView mav = null;

        if (reportType.equals(AppSettings.SELF_FEE_REPORT)) {
            mav = generateSelfFeeReport(criteria);
        } else if (reportType.equals(AppSettings.ALL_FEES_REPORT)) {
            mav = generateAllFeesReportReport(criteria);
        } else if (reportType.equals(AppSettings.CUSTOM_FEE_REPORT)) {
            mav = generateCustomFeesReportReport(criteria);
        } else {
            throw new RuntimeException("Undefined Report Identificator");
        }

        // Retain criteria when generate report
        if (SecurityContextUser.isAdmin()) {
            payees = transactionUserAmountService.findPayeesOfProvider(criteria.getProviderId());
            mav.addObject("providers", providers);
        }

        mav.addObject("payees", payees);
        mav.addObject("criteria", criteria);
        mav.addObject("statuses", getStatuses());
        mav.addObject("reportTypes", ReportTypes);
        mav.addObject("selectedFee", TransactionDetail.FEE_COLUMN_NAMES);

        return mav;
    }

    /**
     * Return data for Self Fee Report
     */
    private ModelAndView generateSelfFeeReport(ReportGenerationCriteria criteria) {
        ModelAndView      result     = new ModelAndView(RequestConstants.ReportsRequests.VIEW_SELF_FEE_REPORT);
        SelfFeeReportData reportData = reportService.getSelfFeeReportData(criteria);

        result.addObject("selfFeeReportData", reportData);

        return result;
    }

    /**
     * Return the current user ID
     */
    private Integer getCurrentUserId(String userEmail) {
        User user = userService.getByEmail(userEmail);

        return user.getId();
    }

    /**
     * Return data for All Fee report
     */
    private ModelAndView generateAllFeesReportReport(ReportGenerationCriteria criteria) {
        ModelAndView     result     = new ModelAndView(RequestConstants.ReportsRequests.VIEW_ALL_FEES_REPORT);
        AllFeeReportData reportData = reportService.getCustomFeesReportData(criteria,
                                          TransactionDetail.FEE_COLUMN_NAMES);

        result.addObject("reportData", reportData);

        return result;
    }

    /**
     * Return data for Custom Fee report
     */
    private ModelAndView generateCustomFeesReportReport(ReportGenerationCriteria criteria) {
        ModelAndView result      = new ModelAndView(RequestConstants.ReportsRequests.VIEW_ALL_FEES_REPORT);
        String[]     selectedFee = criteria.getSelectedFee();

        if (selectedFee != null) {
            AllFeeReportData reportData = reportService.getCustomFeesReportData(criteria, Arrays.asList(selectedFee));

            result.addObject("reportData", reportData);
        }

        return result;
    }
}



